# -*- coding: utf-8 -*-
import os, socket, math
import settings, tuio, movie, OSC
from gameEngine import SuperSprite

class TuioClient:
    def __init__(self, listener, *messages):
        self.listener = listener
        self.object_list =  {}
        self.alive_objects = []
        self.new_objects = []
        self.alive_cursors = []
        self.new_cursors = []
        self.current_frame = 0
        self.last_frame = 0
        self.actions = {
            'set': self._set,
            'alive': self._alive,
            'fseq': self._fseq
        }
        self.setup()
        for message in messages:
            self.manager.add(self.accept_message, message)
    
    def setup(self):
        self.manager = OSC.CallbackManager()
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.socket.bind((settings.HOST, settings.PORT))
        self.socket.setblocking(0)
    
    def has_new_frame(self):
        return self.current_frame >= self.last_frame
    
    def _set(self, message):
        """ the state of each alive but unchanged object is periodically
            resent with additional set messages
        """
        s_id, f_id, pos_x, pos_y, angle, mot_x, mot_y, rot_vector, mot_accel, \
            rot_accel = message[3:]
        
        if s_id not in self.object_list:
            self.object_list[s_id] = f_id
            self.listener.add_tuio_obj(s_id, f_id)
        self.listener.update_tuio_obj(s_id, f_id, pos_x, pos_y, angle, mot_x, \
            mot_y, rot_vector, mot_accel, rot_accel)
    
    def _alive(self, message):
        """ alive message contains the session IDs of all currently
            alive tangible objects
        """
        if self.has_new_frame():
            for obj in message[3:]:
                self.new_objects.append(obj)
                if obj in self.alive_objects:
                    try:
                        self.alive_objects.remove(obj)
                    except:
                        pass
            for obj in self.alive_objects:
                s_id, f_id = obj, self.object_list.pop(obj)
                try:
                    self.listener.remove_tuio_obj(s_id, f_id)
                except:
                    pass
            temp = self.alive_objects
            self.alive_objects = self.new_objects
            self.new_objects = temp
            del self.new_objects[:]
    
    def _fseq(self, message):
        """ fseq messages associates a unique frame id with a set of set
            and alive messages
        """
        self.last_frame = self.current_frame
        self.current_frame = message[3]
        if self.current_frame > self.last_frame:
            self.listener.refresh()
    
    def accept_message(self, *incoming):
        message = incoming[0]
        if len(message) > 1:
            address, command = message[0], message[2]
            if address.endswith("2Dobj"):
                if command in self.actions:
                    self.actions[command](message)

class TuioLabel(SuperSprite):
    def __init__(self, scene, s_id, f_id):
        SuperSprite.__init__(self, scene)
        self.scene = scene
        self.s_id, self.f_id = s_id, f_id

        self.width = 50
        self.center = self.width/2
        self.xpos = 0.0
        self.ypos = settings.TRACKING_TOP
        
        self.setBoundAction(self.HIDE)
        if self.f_id in settings.WORDS:
            token_path = os.path.join(settings.TOKEN_SKELETON % self.f_id)
            self.setImage(token_path)
            self.scene.labelsprites.add(self)
    
    def refresh(self, x, y):
        self.setPosition((x, settings.TRACKING_TOP-25))

class TuioObject(SuperSprite):
    def __init__(self, scene, s_id, f_id):
        SuperSprite.__init__(self, scene)
        self.setImage(settings.TOKEN_IMAGE)
        self.setBoundAction(self.HIDE)
        self.scene = scene
        
        self.width = 50
        self.center = self.width/2
        self.half_circle = 180//math.pi
        
        self.s_id, self.f_id = s_id, f_id
        self.xpos = self.ypos = 0.0
        self.mot_speed = self.mot_accel = 0.0
        self.rot_speed = self.rot_accel = 0.0
        
        self.increment = 0
        self.speed = 0
        self.tolerance = 5.0
        
    def is_moved(self, x, y):
        delta_x = x-self.center-self.x
        delta_y = y-self.center-self.y
        return delta_x!=0 or delta_y!=0
    
    def is_turned(self, angle):
        return angle-self.dir != 0
    
    def refresh(self, x, y, angle, mot_speed, rot_speed, mot_accel, rot_accel):
        """ check which segment has been turned to """
        if settings.FLIP:
            y = self.scene.height - y
            
        # if x or y was changed move the object there
        if self.is_moved(x, y):
            self.setPosition((x, y))
        
        # new angle of the object in degrees
        new_angle = angle*self.half_circle
        
        # decide which direction the object was rotated
        if self.dir == 0:
            self.increment = None
        elif new_angle >= self.dir + self.tolerance:
            self.increment = -1
        elif new_angle <= self.dir - self.tolerance:
            self.increment = 1
        else:
            self.increment = 0
        
        # if the angle was turn the object
        if self.is_turned(angle):
            self.setAngle(new_angle)
        
        # whatever we needs this for (later)
        self.mot_speed, self.mot_accel = mot_speed, mot_accel
        self.rot_speed, self.rot_accel = rot_speed, rot_accel
